#include <buola/gui.h>

#include <buola/gui/caccelerator.h>
#include <buola/gui/ctoolkit.h>
#include <buola/gui/cwindow.h>
#include <buola/gui/cwindowmanager.h>

namespace buola { namespace gui {

std::vector<CAccelerator*> CAccelerator::mAllAccelerators;

void CAccelerator::Set(const TKey &pKey,EKeyMod pMods,EFlags pFlags,const PWindow &pWindow)
{
    if(mValid)
        Unset();

    mKey=pKey;
    mMods=pMods;
    mFlags=pFlags;
    mWindow=pWindow.get();
    
    if(mFlags&GLOBAL)
    {
        get_toolkit()->GrabKey(mKey,mMods,
                        mFlags&PROCESSNORMAL);
    }
    else if((mFlags&LOCAL)||(mFlags&TOPLEVEL))
    {
        if(!mWindow)
        {
            throw XMismatch("can't set a local or toplevel accelerator without a window");
        }
    }

    mValid=true;
}

void CAccelerator::Unset()
{
    if(!mValid) return;
    
    if(mFlags&GLOBAL)
    {
        get_toolkit()->UngrabKey(mKey,mMods);
    }

    mValid=false;
}

bool CAccelerator::Check(const CKeyEvent &pE)
{
    if(!mValid) return false;
    if(!(pE.mKey==mKey)) return false;
    if(pE.mMods!=KEYMOD_ANY&&pE.mMods!=mMods) return false;

    if(mFlags&LOCAL)
    {
        // only report if the focus window is a child of the grab window
        if(!window_manager().FocusWindow()->IsDescendantOf(*mWindow)&&
            (!mWindow->IsTopLevel()||!mWindow->IsActive())) return false;
    }
    else if(mFlags&TOPLEVEL)
    {
        // only report if the toplevel window is active
        if(!mWindow->IsActive()) return false;
    }

    return true;
}

//! \return true if the event should be normally dispatched too
//!

bool CAccelerator::Dispatch(CKeyEvent &pE)
{
    CAccelerator *lAccel=nullptr;

    for(int i=0;i<mAllAccelerators.size();i++)
    {
        if(mAllAccelerators[i]->Check(pE))
        {
            if(!lAccel||(mAllAccelerators[i]->mWindow&&lAccel->mWindow&&
                mAllAccelerators[i]->mWindow->IsDescendantOf(*lAccel->mWindow)))
            {
                lAccel=mAllAccelerators[i];
            }
        }
    }
    
    if(!lAccel) return true;
    
    if(pE.mType==CKeyEvent::PRESS)
        lAccel->eTriggered();
    
    if(lAccel->mWindow)
    {
        if(pE.mType==CKeyEvent::PRESS)
            lAccel->mWindow->OnKeyDown(pE);
        else if(pE.mType==CKeyEvent::RELEASE)
            lAccel->mWindow->OnKeyUp(pE);
    }
    
    return lAccel->mFlags&PROCESSNORMAL;
}

std::wstring CAccelerator::GetKeyName()
{
    std::wstring lReturn;
    
    if(mMods&KEYMOD_CONTROL)
        lReturn+=L"ctrl+";
    if(mMods&KEYMOD_SHIFT)
        lReturn+=L"shift+";
    if(mMods&KEYMOD_ALT)
        lReturn+=L"alt+";
    if(mMods&KEYMOD_ALTGR)
        lReturn+=L"alt gr+";
    if(mMods&KEYMOD_WIN)
        lReturn+=L"win+";
    lReturn+=mKey.GetName();
    return lReturn;
}

/*namespace gui*/ } /*namespace buola*/ }
